package com.ld.shieldsb.common.core.reflect;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;

import com.ld.shieldsb.common.core.exception.ReflectException;
import com.ld.shieldsb.common.core.util.StringUtils;
import com.ld.shieldsb.common.core.util.TypeUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 需要java8支持
 * 
 * @ClassName FunctionUtil
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2019年7月24日 下午5:53:49
 *
 */
@Slf4j
public class FunctionUtil {
    public static final boolean QUERY_LAMBDAS_SUPPORT = TypeUtil.isGeJdk8(); // 是否是java8以上，lambda表达式需要java8以上
    private static final Map<Function<?, ?>, String> FIELDNAME_NAMES = new HashMap<>(); // Property和名称的对应缓存
    private static final Map<Function<?, ?>, String> FUNCTION_NAMES = new HashMap<>(); // Property和字段名称的对应缓存
    private static final Map<Function<?, ?>, Field> FUNCTION_FIELDS = new HashMap<>(); // Property和字段的对应缓存
    private static final Map<Function<?, ?>, String> FUNCTION_CLASS_METHODS = new HashMap<>(); // Property和类、方法的对应缓存

    // 序列化以便取R的名称（即方法名）
    public interface Property<T, R> extends Function<T, R>, Serializable {
    }

    // 3参数，序列化以便取R的名称（即方法名）
    public interface BiProperty<T, U, R> extends BiFunction<T, U, R>, Serializable {
    }

    /**
     * 根据Function获取属性的名称
     * 
     * @Title getFieldName
     * @author 吕凯
     * @date 2019年7月1日 上午11:10:44
     * @param property
     * @return String
     */
    public static <T> String getFieldName(Property<T, ?> property) {
        String attr = null;
        if (QUERY_LAMBDAS_SUPPORT) {
            attr = FIELDNAME_NAMES.get(property);
            if (attr == null) {
                String method = getFunctionsName(property);
                attr = getFieldNameByMethodName(method);
                FIELDNAME_NAMES.put(property, attr);
                return attr;
            }
        } else {
            log.warn(TypeUtil.NEED_JAVA8);
        }
        return attr;

    }

    /**
     * 
     * 获取参数名称
     * 
     * @Title getFieldNameByMethodName
     * @author 吕凯
     * @date 2022年1月20日 上午10:13:54
     * @param attr
     * @param method
     * @return String
     */
    public static String getFieldNameByMethodName(String method) {
        String attr = null;
        if (StringUtils.isNotEmpty(method)) {
            if (method.startsWith("is")) { // is开头的
                attr = method.substring(2);
            } else { // get、has开头的
                attr = method.substring(3);
            }
            attr = StringUtils.uncap(attr); // 首字母转小写
        }
        return attr;
    }

    /**
     * 获取Function的名称
     * 
     * @Title getFunctionsName
     * @author 吕凯
     * @date 2021年4月25日 下午5:12:41
     * @param function
     * @return String
     */
    public static <T> String getFunctionsName(Property<T, ?> function) {
        String attr = null;
        if (QUERY_LAMBDAS_SUPPORT) {
            attr = FUNCTION_NAMES.get(function);
            if (attr == null) {
                try {
                    SerializedLambda serializedLambda = getSerializedLambda(function);
                    String method = serializedLambda.getImplMethodName();
                    attr = method;
                    FUNCTION_NAMES.put(function, attr);
                    return attr;
                } catch (ReflectiveOperationException e) {
                    throw new ReflectException("Function获取名称异常", e);
                }
            }
        } else {
            log.warn(TypeUtil.NEED_JAVA8);
        }
        return attr;
    }

    /**
     * 获取类方法名称
     * 
     * @Title getFunctionClassMethod
     * @author 吕凯
     * @date 2021年4月26日 上午10:31:38
     * @param <T>
     * @param function
     * @return String
     */
    public static <T> String getFunctionClassMethod(Property<T, ?> function) {
        String attr = null;
        if (QUERY_LAMBDAS_SUPPORT) {
            attr = FUNCTION_CLASS_METHODS.get(function);
            if (attr == null) {
                try {
                    SerializedLambda serializedLambda = getSerializedLambda(function);
                    String method = serializedLambda.getImplMethodName();
                    String className = serializedLambda.getImplClass().replace("/", ".");
                    attr = className + " " + method;
                    FUNCTION_CLASS_METHODS.put(function, attr);
                    return attr;
                } catch (ReflectiveOperationException e) {
                    throw new ReflectException("Function获取名称异常", e);
                }
            }
        } else {
            log.warn(TypeUtil.NEED_JAVA8);
        }
        return attr;
    }

    /**
     * 获取Function对应的field对象
     * 
     * @Title getFiled
     * @author 吕凯
     * @date 2021年4月26日 上午9:22:29
     * @param function
     * @return Field
     */
    public static <T> Field getFiled(Property<T, ?> function) {
        Field field = null;
        if (QUERY_LAMBDAS_SUPPORT) {
            field = FUNCTION_FIELDS.computeIfAbsent(function, key -> {
                Field mapField = null;
                try {
                    SerializedLambda serializedLambda = getSerializedLambda(function);
                    String fieldName = getFieldName(function);
                    mapField = Class.forName(serializedLambda.getImplClass().replace("/", ".")).getDeclaredField(fieldName); // 可能为空
                } catch (ReflectiveOperationException e) {
                    log.warn("", e);
                }
                return mapField;
            });
        } else {
            log.warn(TypeUtil.NEED_JAVA8);
        }
        return field;
    }

    /**
     * 获取序列化的lambda表达式
     * 
     * @Title getSerializedLambda
     * @author 吕凯
     * @date 2021年4月26日 上午9:25:43
     * @param function
     * @return
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     *             SerializedLambda
     */
    private static SerializedLambda getSerializedLambda(Property<?, ?> function)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Method writeReplaceMethod = function.getClass().getDeclaredMethod("writeReplace"); // 此方法只有序列化对象有
        // 从序列化方法取出序列化的lambda信息
        boolean isAccessible = writeReplaceMethod.isAccessible();
        writeReplaceMethod.setAccessible(Boolean.TRUE);
        SerializedLambda serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(function);
        writeReplaceMethod.setAccessible(isAccessible);
        return serializedLambda;
    }

}
